package com.ruyue.csinstall.utils;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import java.io.*;
import java.util.*;

/**
 * @author xiawl
 * @create 2017/7/23 16:04.
 */
public class FileTools {
    private static final Log log = LogFactory.getLog(FileTools.class);

    /**
     * 新旧filetransfer-config.xml作对比,以旧的该文件为主体将升级包里新增的节点
     * 同步到旧文件里去,并覆盖升级包里的该文件.
     * @param src
     * @param dist
     * @return
     */
    public static boolean mergeFiletransferXML(String src, String dist){

        try {

            SAXReader new_reader = new SAXReader();
            Document new_doc = new_reader.read(dist);

            SAXReader old_reader = new SAXReader();
            Document old_doc = old_reader.read(src);


            List<Element> over = new ArrayList<Element>();
            String xpathConvert = "root/partner";
            List<Element> ele = new_doc.selectNodes(xpathConvert);
            for(Element e : ele){
                String comefrom = e.attributeValue("comefrom");
                List<Element> oele = old_doc.selectNodes(String.format("%s[@comefrom='%s']", xpathConvert, comefrom));
                if(oele.isEmpty()){
                    log.debug(String.format("[%s]在旧版本基础上新增了节点[%s]", dist, e.asXML()));
                    over.add(e);
                }
            }

            for(int i = 0; i < over.size(); i++){
                Element onode = (Element) old_doc.selectSingleNode("root/partner[last()]");
                onode.getParent().elements().add(over.get(i).clone());
            }

            OutputFormat opf = OutputFormat.createPrettyPrint();
            opf.setEncoding("UTF-8");
            opf.setIndentSize(4);
            opf.setExpandEmptyElements(true);
            XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(dist), opf);
            xmlWriter.write(old_doc);
            xmlWriter.close();

        } catch (Exception e) {

        }

        return false;
    }

    public static boolean mergeConvertXML(String src, String dist) {

        try {

            boolean mod = false;

            SAXReader new_reader = new SAXReader();
            Document new_doc = new_reader.read(dist);

            SAXReader old_reader = new SAXReader();
            Document old_doc = old_reader.read(src);

            String xpathConvert = "root/property/column";
            List<Element> ele = new_doc.selectNodes(xpathConvert);
            for (Element e : ele) {
                String columName = e.attributeValue("name");

                //xxxSets
                List<Element> sub = e.selectNodes(String.format("%sSet", columName));
                for (Element ee : sub) {
                    String set_name = ee.attributeValue("name"), set_value = ee.attributeValue("value"), set_describe = ee.attributeValue("describe");

                    String oldPath = String.format("%s/%sSet[@name='%s']", xpathConvert, columName, set_name);
                    List<Element> oldElements = old_doc.selectNodes(oldPath);

                    if(!oldElements.isEmpty()){
                        Element oldElement = oldElements.get(0);
                        String old_value = oldElement.attributeValue("value"), old_describe = oldElement.attributeValue("describe");

                        if (!set_value.equals(old_value)) {
                            log.debug(String.format("[%s]~[%s]->[%s]", dist, ee.asXML(), oldElement.asXML()));
                            ee.attribute("value").setValue(old_value);
                            mod = true;
                        }
                    }
                }

                //column
                String columValue = e.attributeValue("value"), columnDescribe = e.attributeValue("describe");
                String oldPath = String.format("%s[@name='%s']", xpathConvert, columName);
                List<Element> oldElements = old_doc.selectNodes(oldPath);
                Element oldColumn = oldElements.get(0);
                String oldColumnValue = oldColumn.attributeValue("value"), oldColumnDescribe = oldColumn.attributeValue("describe");

                if (columValue != null && oldColumnValue != null && !columValue.equals(oldColumnValue)) {
                    log.debug(String.format("[%s]~[%s]->[%s]", dist, e.asXML(), oldColumn.asXML()));
                    e.attribute("value").setValue(oldColumnValue);
                    mod = true;
                }
            }

            if(mod){
                OutputFormat opf = OutputFormat.createPrettyPrint();
                opf.setEncoding("UTF-8");
                opf.setIndentSize(4);
                org.dom4j.io.XMLWriter xmlWriter = new org.dom4j.io.XMLWriter(new FileOutputStream(dist), opf);
                xmlWriter.write(new_doc);
                xmlWriter.close();
                log.debug(String.format("[%s]文件发生了合并", dist));
            }
            return true;
        } catch (Exception e) {
            log.error(String.format("合并[%s]出错,需要手动配置;该问题通常因为[%s]不存在或者第一次布署不适用于升级操作", dist, src));
        }
        return false;
    }

    /**
     * 合并属性配置文件
     * @param src
     * @param dist
     * @return
     */
    public static boolean mergeProperties(String src, String dist){
        try {
            PropertiesConfiguration prosrc = new PropertiesConfiguration(src);
            PropertiesConfiguration prodist = new PropertiesConfiguration(dist);
            Iterator<String> it = prodist.getKeys();
            boolean mod = false;
            while(it.hasNext()){
                String dk = it.next();

                Object dv = prodist.getProperty(dk);
                Object sv = prosrc.getProperty(dk);
                String dvstr = dv.toString();

                if(sv != null){
                    String svstr = sv.toString();
                    if(!dvstr.equals(svstr)){
                        log.debug(String.format("[%s]~[%s=%s]->[%s=%s]", dist, dk, dvstr, dk, svstr));
                        prodist.setProperty(dk, (sv instanceof List) ? (svstr.substring(1, svstr.length() -1)) : svstr);
                        mod = true;
                    }
                }
            }

            if(mod){
                log.debug(String.format("[%s]文件发生了合并", dist));
                prodist.save();
            }

            return true;
        } catch (ConfigurationException e) {
            log.error(String.format("合并[%s]出错,需要手动配置;该问题通常因为[%s]不存在或者第一次布署不适用于升级操作", dist, src));
        }
        return false;
    }

    /**
     * 拷贝证书之类的文件
     * @param srcPath
     * @param distPath
     * @throws IOException
     */
    public static void cpNoOverlap(String srcPath, final String distPath) throws IOException {
        File srcFile = new File(srcPath);
        File distFile = new File(distPath);
        final List<String> distlist = fnames(distPath, true);
        if (srcFile.isDirectory()) {
            FileUtils.copyDirectory(srcFile, new File(distFile, srcFile.getName()), new FileFilter() {
                @Override
                public boolean accept(File xfile) {
                    String filename = xfile.getName();
                    boolean accept = !distlist.contains(filename) && !filename.contains(".bak");
                    if(accept){
                        if(xfile.isFile()){
                            log.debug(String.format("拷贝文件[%s]", xfile.getAbsolutePath()));
                        }
                    }
                    return accept;
                }
            }, true);
        } else if (srcFile.isFile()) {
            FileUtils.copyFileToDirectory(srcFile, distFile, true);
        }
    }

    public static List<String> fnames(String filePath, boolean loop) {
        List<String> lsn = new ArrayList<String>();
        List<File> list = ls(filePath, loop);
        for(File f : list){
            if(f.isFile()){
                lsn.add(f.getName());
            }
        }
        return lsn;
    }

    /**
     * ls 遍历文件
     *
     * @param filePath
     * @param loop
     * @return
     */
    public static List<File> ls(String filePath, boolean loop) {
        List<File> list = new ArrayList();
        try {
            File file = new File(filePath);
            list.addAll(ls(file, loop));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return list;
    }

    /**
     * ls 遍历文件
     *
     * @param file
     * @param loop
     * @return
     */
    public static List<File> ls(File file, boolean loop) {
        List<File> list = new ArrayList();
        try {
            list.add(file);
            if (!file.isDirectory()) {
                list.add(file);
            } else if (file.isDirectory()) {
                File[] subList = file.listFiles();
                subList = filesSort(subList, true);
                for (File subFile : subList) {
                    if (subFile.isDirectory() && loop) {
                        list.addAll(ls(subFile.getPath(), loop));
                    } else {
                        list.add(subFile);
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return list;
    }

    private static File[] filesSort(File[] inFiles, boolean asc) {
        List<String> files = new ArrayList();
        List<String> dirs = new ArrayList();
        for (File subFile : inFiles) {
            if (subFile.isDirectory()) {
                dirs.add(subFile.getPath());
            } else if (subFile.isFile()) {
                files.add(subFile.getPath());
            }
        }
        String[] fileArray = {};
        if (files.size() > 0) {
            fileArray = list2Array(files);
            Arrays.sort(fileArray);
            if (!asc) {
                Arrays.sort(fileArray, Collections.reverseOrder());
            }
        }
        String[] dirArray = {};
        if (dirs.size() > 0) {
            dirArray = list2Array(dirs);
            Arrays.sort(dirArray);
            if (!asc) {
                Arrays.sort(dirArray, Collections.reverseOrder());
            }
        }
        return concat2FileArray(fileArray, dirArray);
    }

    /**
     * list2Array 列表转换数组
     *
     * @param list
     * @return
     */
    private static String[] list2Array(List list) {
        String array[] = (String[]) list.toArray(new String[list.size()]);
        return array;
    }

    /**
     * concat2FileArray 合并文件数组
     *
     * @param old1
     * @param old2
     * @return
     */
    private static File[] concat2FileArray(String[] old1, String[] old2) {
        File[] newArray = new File[old1.length + old2.length];
        for (int i = 0, n = old1.length; i < n; i++) {
            newArray[i] = new File(old1[i]);
        }
        for (int i = 0, j = old1.length, n = (old1.length + old2.length); j < n; i++, j++) {
            newArray[j] = new File(old2[i]);
        }
        return newArray;
    }
}